
import {
    Config,
    Flags,
    Logger,
    NumericParameters,
    PixelStreaming,
    TextParameters
} from '@epicgames-ps/lib-pixelstreamingfrontend-ue5.4';
import { createGuid, Destroyable } from 'xbsj-base';
import { ESMsgWebSocketWrapper } from './ESMsgWebSocketWrapper';
import { SEND } from './dataFormats';

// @ts-ignore
window.g_emitDescriptor_maxBytes = 30000;
let lastSendId = -1;
//越小，输出的信息越少
Logger.SetLoggerVerbosity(1);
export class PSUEViewer extends Destroyable {

    private _eventTarget = new EventTarget();
    get eventTarget() { return this._eventTarget; }

    addEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: AddEventListenerOptions | boolean) {
        return this.eventTarget.addEventListener(type, callback, options);
    }

    removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean) {
        return this.eventTarget.removeEventListener(type, callback, options);
    }

    dispatchEvent(event: Event) {
        return this.eventTarget.dispatchEvent(event);
    }

    public debug = false;

    private uiInteractionCallbacks = new Map<string, any>();

    private _url = this._options.url;

    private _container = (() => {
        const c = typeof this._domOrDomId === 'string' ? document.getElementById(this._domOrDomId) : this._domOrDomId;
        if (!c) throw new Error(`ESUEViewer container${this._domOrDomId} error!`);
        return c;
    })();
    get container() { return this._container; }

    private _pixelStream = (() => {
        const config = new Config({
            initialSettings: {
                AutoPlayVideo: true,
                AutoConnect: true,
                StartVideoMuted: true,
                WaitForStreamer: true,
                SuppressBrowserKeys: false,
                [TextParameters.SignallingServerUrl]: this._url,
            }
        })
        config.setFlagEnabled(Flags.BrowserSendOffer, false);
        config.setFlagEnabled(Flags.HoveringMouseMode, true);
        config.setFlagEnabled(Flags.MatchViewportResolution, true);
        //这个重复次数，有可能服务端的streamer还未连接
        // config.setNumericSetting(NumericParameters.MaxReconnectAttempts, 100);
        config.setNumericSetting(NumericParameters.MaxReconnectAttempts, 0);
        // Create a stream and UI
        //const stream = new PixelStreaming(config, { videoElementParent: this.container });
        const stream = new PixelStreaming(config, { videoElementParent: this._options.createUI ? undefined : this.container });

        this.d(() => { (!this.isDestroyed()) && stream.disconnect(); });

        return stream;
    })();
    get pixelStream() { return this._pixelStream; }

    private _esMsgWsWrapper?: ESMsgWebSocketWrapper;
    private _esMsgWsWrapperInit = (() => {
        if (!this._options.esmsgWsUri) return;
        this._esMsgWsWrapper = this.disposeVar(new ESMsgWebSocketWrapper(this, this._options.esmsgWsUri));
        // this._esMsgWsWrapper = undefined;
    })();
    get esMsgWsWrapper() { return this._esMsgWsWrapper; }

    get esWebSocketReady() {
        return this.esMsgWsWrapper?.inner.readyState === 1;
    }

    constructor(
        private _domOrDomId: HTMLDivElement | string,
        private _options: { url: string, esmsgWsUri?: string | undefined, createUI?: boolean | undefined }) {
        super();
        const esueViewer = this;
        if (esueViewer.esMsgWsWrapper) {
            // @ts-ignore
            const originSendData = this.webRtcController.sendMessageController.dataChannelSender.sendData;
            this.webRtcController.sendMessageController.dataChannelSender.constructor.prototype.sendData = function (data: ArrayBuffer) {

                if (esueViewer.esMsgWsWrapper) {
                    esueViewer.esMsgWsWrapper.sendInputData(data);
                } else {
                    originSendData.call(this, data);
                }

            };
        }

        //视频初始化成功
        this.pixelStream.addEventListener("videoInitialized", () => {

            //发送视频加载成功事件
            console.log("%c[EarthSDK webrtc videoInitialized]", "background: #a6ec99; color: black", `videoInitialized`);
            this.dispatchEvent(new CustomEvent('videoinited', { detail: 'videoinited' }))
        });

        //监控webrtc连接失败事件
        this.pixelStream.addEventListener("webRtcFailed", () => {
            if (!this.isDestroyed()) {
                console.log("%c[EarthSDK webRtc webRtcFailed]", "background: red; color: black", `webRtcFailed`);
                this.dispatchEvent(new CustomEvent('error', { detail: 'webRtcFailed' }));
                this.pixelStream.disconnect();
            }
            // console.log("earthsdk esviewer webRtcFailed");
        });
        //监控信令连接失败事件
        this.pixelStream.addEventListener("webRtcDisconnected", () => {
            if (!this.isDestroyed()) {
                console.log("%c[EarthSDK webRtc webRtcDisconnected]", "background: red; color: black", `webRtcDisconnected`);
                this.dispatchEvent(new CustomEvent('error', { detail: 'webRtcDisconnected' }));
                this.pixelStream.disconnect();
            }
            // console.log("earthsdk esviewer webRtcDisconnected");
        });

        //监控信令连接成功事件
        this.pixelStream.addEventListener("webRtcConnected", () => {
            if (!this.isDestroyed()) {
                console.log("%c[EarthSDK webrtc webRtcConnected]", "background: #a6ec99; color: black", `webRtcConnected`);
                this.dispatchEvent(new CustomEvent('webRtcConnected', { detail: 'webRtcConnected' }));
            }
            // console.log("earthsdk esviewer webRtcConnected");
        });


        //处理ue事件
        this.pixelStream.addResponseEventListener('esueviewer', (response: string) => {
            this.processResponse(response);
        });
        this.dispose(() => {
            //关闭连接
            if (!this._options.createUI)
                this.container.removeChild(this.webRtcController.videoPlayer.getVideoElement());
            this.pixelStream.disconnect();
            this.pixelStream.removeResponseEventListener('esueviewer');
        });
    }

    //处理回调函数
    resolveCallback(text: string) {
        //1， 解析text失败，直接返回
        let retObj = {}
        try {
            retObj = JSON.parse(text)
        } catch (error) {
            return false
        }

        //2, 获取 调用id
        // TODO
        // @ts-ignore
        let callid = retObj.callid as string;
        if (typeof callid !== 'string') {
            return false
        }

        //3，寻找resolve方法，如果没有，那么也返回false
        if (!this.uiInteractionCallbacks.has(callid)) return false

        //类型不对，返回false
        let callback = this.uiInteractionCallbacks.get(callid)
        if (typeof callback !== 'function') {
            return false
        }

        // 成功调用，返回true
        callback(retObj)
        this.uiInteractionCallbacks.delete(callid)
        return true
    }

    //处理ue发送过来的事件
    processResponse(text: string) {

        // vtxf add 20230322
        if (this.debug ?? false) {

            try {
                const json = JSON.parse(text);
                // @ts-ignore
                json.ers = json.ers && json.ers.filter(e => e.et !== 'mouseMoveEvent');
                if (!((json.ers && json.ers.length > 0) || (json.frs && json.frs.length > 0))) return;

                console.group(
                    "%cUE => JS",
                    " background-color:#006EFF;color:#ffffff;font-weight:bold;padding:4px;border-radius:5px;"
                );
                console.log(
                    `%c${JSON.stringify(json, undefined, '')}`,
                    " border:1px dashed #006EFF;border-radius:5px;padding:10px;line-height:25px;color:#006EFF;"
                );
                console.groupEnd();

            } catch (error) {
                console.error(`processResponse(${text}) error: ${error}`, error);
            }
        }

        // 如果成功处理调用回调
        if (this.resolveCallback(text)) return;
        // 没有处理成功，直接发送普通的response 事件
        this.dispatchEvent(new CustomEvent('ueevent', { detail: text }))

    }

    //  发送UI interface
    emitUIInteraction(descriptor: any, callback: any) {
        if (typeof callback == 'function') {
            //生成调用id
            descriptor.callid = createGuid()

            //记录回调函数
            this.uiInteractionCallbacks.set(descriptor.callid, callback)
        }
        // this.emitDescriptor(SEND.UIInteraction, descriptor)
        this.pixelStream.emitUIInteraction(descriptor)
    }

    private get webRtcController() {
        // @ts-ignore
        const rtcController = this.pixelStream._webRtcController;
        return rtcController;
    }

    private _pixelStreamEmitUIInteractionAsString(str: string) {
        // @ts-ignore
        this.webRtcController.streamMessageController.toStreamerHandlers.get(
            'UIInteraction'
        )([str]);
    }

    //  发送UI interface
    emitUIInteractionForBigData(descriptor: any, callback: any) {
        if (typeof callback == 'function') {
            // 生成调用id
            descriptor.callid = createGuid();
            // 记录回调函数
            this.uiInteractionCallbacks.set(descriptor.callid, callback);
        }

        // json对象转字符串
        const descriptorAsString = JSON.stringify(descriptor)
        // @ts-ignore
        const maxBytes = window.g_emitDescriptor_maxBytes as number;
        const l = descriptorAsString.length

        const currentSendId = ++lastSendId;

        //不管是不是独立通信，都使用emitDescriptorWithString发送消息，但是最大截断为maxBytes 30000
        if (this.esMsgWsWrapper) {
            if (l < maxBytes) {
                this.esMsgWsWrapper.emitDescriptorWithString(SEND.UIInteraction, `${currentSendId}-0/1-` + descriptorAsString);
                return
            }
            // 否则就分拆后再发送
            const n = Math.ceil(l / maxBytes) | 0
            const ds = [...new Array(n).keys()].map(i => descriptorAsString.slice(i * maxBytes, (i + 1) * maxBytes))
            ds.forEach((e, i) => {
                this.esMsgWsWrapper && this.esMsgWsWrapper.emitDescriptorWithString(SEND.UIInteraction, `${currentSendId}-${i}/${n}-` + e)
            })
        }

        // if (this.esMsgWsWrapper && this.esMsgWsWrapper.inner &&
        //     this.esMsgWsWrapper.inner.readyState === WebSocket.OPEN) { // 如果存在独立的ws消息，那么就不分拆消息
        //     this.esMsgWsWrapper.emitDescriptorWithString(SEND.UIInteraction, `${currentSendId}-0/1-` + descriptorAsString);
        //     return;
        // }

        // // 不超过30000走一次搞定
        // // if (l < maxBytes) {
        // if (l < maxBytes) {
        //     this._pixelStreamEmitUIInteractionAsString(`${currentSendId}-0/1-` + descriptorAsString)
        //     return
        // }
        // // 否则就分拆后再发送
        // const n = Math.ceil(l / maxBytes) | 0
        // const ds = [...new Array(n).keys()].map(i => descriptorAsString.slice(i * maxBytes, (i + 1) * maxBytes))
        // ds.forEach((e, i) => this._pixelStreamEmitUIInteractionAsString(`${currentSendId}-${i}/${n}-` + e))
    }

    // UE4 内置命令 目前支持两种
    // 1. 控制台命令:
    //    "{ ConsoleCommand: <string> }"
    //
    // 2. 设置分辨率.
    //    "{ Resolution.Width: <value>, Resolution.Height: <value> } }"
    //
    emitCommand(descriptor: object) {
        // this.emitDescriptor(SEND.Command, descriptor)
        this.pixelStream.emitCommand(descriptor);
    }

    getVideoSize() {
        const ve = this.webRtcController.videoPlayer.getVideoElement();
        return { width: ve.videoWidth, height: ve.videoHeight };
    }

    resizeUEVideo(x: number, y: number) {
        let descriptor = {
            Resolution: {
                Width: x,
                Height: y
            }
        };
        console.log(`resizeUEVideo emitCommand ${x} ${y} begin`);
        this.emitCommand(descriptor);
    }
}
